package cn.jolyoulu.common.web.security.service;

import cn.jolyoulu.common.core.utils.other.IdUtils;
import cn.jolyoulu.common.redis.utils.RedisUtils;
import cn.jolyoulu.common.web.excption.GlobalException;
import cn.jolyoulu.common.web.security.constant.SecurityConstants;
import cn.jolyoulu.common.web.security.entity.LoginUser;
import cn.jolyoulu.common.web.security.entity.TokenInfo;
import cn.jolyoulu.common.web.security.enums.SecurityExpType;
import cn.jolyoulu.common.web.security.utils.AuthUtil;
import cn.jolyoulu.common.web.security.utils.SecurityUtils;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.Time;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * @Author LuZhouJin
 * @Date 2023/3/17
 * token验证处理
 */
@Slf4j
@Component
public class TokenService {

    @Autowired
    private RedisUtils redisUtils;

    /**
     * 过期时间
     */
    private final static long EXPIRE_TIME = 30;

    /**
     * 过期时间类型
     */
    private final static TimeUnit EXPIRE_TIME_TYPE = TimeUnit.MINUTES;

    /**
     * token标识
     */
    private final static String ACCESS_TOKEN = "LOGIN:TOKENS:";


    @Autowired
    private HttpServletRequest request;

    /**
     * 创建令牌
     */
    public TokenInfo createToken(LoginUser<?> loginUser) {
        String uuid = IdUtils.simpleUUID();
        String userId = loginUser.getUser().userId();
        String userName = loginUser.getUser().userName();

        loginUser.setToken(userId);
        loginUser.setUserId(userId);
        loginUser.setUsername(userName);
        loginUser.setIpaddr("");
        // Jwt存储信息
        Map<String, Object> claimsMap = new HashMap<String, Object>();
        claimsMap.put(SecurityConstants.USER_KEY, userId);
        claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
        claimsMap.put(SecurityConstants.DETAILS_UUID, uuid);
        String accessToken = AuthUtil.createToken(claimsMap);
        loginUser.setAccessToken(accessToken);
        refreshToken(loginUser);//刷新令牌有效期,并且把用户信息存到redis
        // 接口返回信息
        TokenInfo tokenInfo = new TokenInfo();
        tokenInfo.setAccessToken(accessToken);
        return tokenInfo;
    }

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser<?> getLoginUser() {
        return getLoginUser(request);
    }

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser<?> getLoginUser(HttpServletRequest request) {
        // 获取请求携带的令牌
        String token = SecurityUtils.getToken(request);
        return getLoginUser(token);
    }

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser<?> getLoginUser(String token) {
        if (StringUtils.isBlank(token)) {
            throw new GlobalException(SecurityExpType.TOKEN_NOT_EXIST);
        }
        String userkey = AuthUtil.getUserKey(token);
        return redisUtils.get(getTokenKey(userkey));
    }

    /**
     * 设置用户身份信息
     */
    public void setLoginUser(LoginUser<?> loginUserInfo) {
        if (Objects.nonNull(loginUserInfo) && StringUtils.isNotEmpty(loginUserInfo.getToken())) {
            refreshToken(loginUserInfo);
        }
    }

    /**
     * 删除用户缓存信息
     */
    public void delLoginUser(String token) {
        if (StringUtils.isNotEmpty(token)) {

            String userkey = AuthUtil.getUserKey(token);
            redisUtils.del(getTokenKey(userkey));
        }
    }

    /**
     * 验证令牌有效期，相差不足120分钟，自动刷新缓存
     *
     * @param loginUser
     */
    public void verifyToken(LoginUser<?> loginUser) {
        String token = SecurityUtils.getToken(request);
        //是否多端登录校验
        if(!token.equals(loginUser.getAccessToken())){
            throw new GlobalException(SecurityExpType.TOKEN_NOT_EXIST.code(),"您的账号已在其他设备登录");
        }
        //过期时间校验
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= EXPIRE_TIME_TYPE.toMillis(EXPIRE_TIME)) {
            refreshToken(loginUser);
        }
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser<?> loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + EXPIRE_TIME_TYPE.toMillis(EXPIRE_TIME));
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(loginUser.getToken());
        redisUtils.set(userKey, loginUser, EXPIRE_TIME, EXPIRE_TIME_TYPE);
    }

    private String getTokenKey(String token) {
        return ACCESS_TOKEN + token;
    }
}